package com.example.backend.service.impl;

import com.example.backend.entity.StorageOut;
import com.example.backend.entity.StockRecord;
import com.example.backend.mapper.StorageOutMapper;
import com.example.backend.service.StorageOutService;
import com.example.backend.service.StockRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.backend.mapper.StorageInMapper;
import com.example.backend.mapper.WarehouseMapper;
import com.example.backend.entity.StorageIn;
import com.example.backend.entity.Warehouse;
import com.example.backend.mapper.CompanyMapper;
import com.example.backend.entity.Company;
import com.example.backend.service.PortRuleCalculationService;
import com.example.backend.service.WarehouseAllocationService;

import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.math.BigDecimal;

@Service
public class StorageOutServiceImpl implements StorageOutService {
    @Autowired
    private StorageOutMapper storageOutMapper;
    @Autowired
    private StorageInMapper storageInMapper;
    @Autowired
    private WarehouseMapper warehouseMapper;
    @Autowired
    private CompanyMapper companyMapper;
    @Autowired
    private PortRuleCalculationService portRuleCalculationService;
    @Autowired
    private WarehouseAllocationService warehouseAllocationService;
    @Autowired
    private StockRecordService stockRecordService;

    @Override
    public int addStorageOut(StorageOut storageOut) {
        double remainWeight = storageOut.getOutWeight().doubleValue();
        storageOut.setInId(null);
        // 使用StockRecord获取该公司在该港口下最早入库的库存记录（FIFO策略）
        List<StockRecord> availableStockRecords = stockRecordService.getAvailableStockRecordsByCompanyAndPort(
            storageOut.getCompanyId(), 
            storageOut.getPortId()
        );
        if (availableStockRecords.isEmpty()) {
            throw new RuntimeException("该公司在该港口下没有可用库存");
        }
        // 计算总存储费并更新库存
        BigDecimal totalStorageFee = processStockOutAndCalculateFee(storageOut, availableStockRecords, remainWeight);
        // 创建一条出库记录（只插入一次）
        StorageOut out = new StorageOut();
        out.setOutDate(storageOut.getOutDate());
        out.setOutPieces(storageOut.getOutPieces());
        out.setOutWeight(storageOut.getOutWeight());
        out.setInId(null); // 禁止指定批次
        out.setOrderNumber(storageOut.getOrderNumber());
        out.setCompanyId(storageOut.getCompanyId());
        out.setPortId(storageOut.getPortId());
        out.setStorageFee(totalStorageFee);
        out.setRemainWeight(BigDecimal.ZERO); // 全部出完，剩余为0
        // 插入出库记录
        storageOutMapper.insert(out);
        return 1;
    }
    
    /**
     * 处理出库并计算存储费
     * @param storageOut 出库请求
     * @param availableStockRecords 可用库存记录
     * @param totalOutWeight 总出库重量
     * @return 总存储费
     */
    private BigDecimal processStockOutAndCalculateFee(StorageOut storageOut, List<StockRecord> availableStockRecords, double totalOutWeight) {
        BigDecimal totalStorageFee = BigDecimal.ZERO;
        double remainWeight = totalOutWeight;

        // 获取免费天数和费率
        Object[] rule = portRuleCalculationService.getEffectiveRule(storageOut.getCompanyId(), storageOut.getPortId());
        int freeDays = (Integer) rule[0];
        BigDecimal feeRate = (BigDecimal) rule[1];

        double totalAvailableStock = availableStockRecords.stream()
                .mapToDouble(record -> record.getRemainingWeight().doubleValue())
                .sum();

        for (StockRecord stockRecord : availableStockRecords) {
            if (remainWeight <= 0) break;

            double availableWeight = stockRecord.getRemainingWeight().doubleValue();
            if (availableWeight <= 0) continue;

            double toOut = Math.min(remainWeight, availableWeight);

            // 计算这部分货物的存储费
            StorageIn storageIn = storageInMapper.selectById(stockRecord.getInId());
            if (storageIn != null) {
                // 修复：使用ChronoUnit计算天数（包含当天）
                long days = ChronoUnit.DAYS.between(
                        storageIn.getInDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(),
                        storageOut.getOutDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
                ) + 1; // +1 包含当天

                // 调试信息
                System.out.println("批次ID: " + stockRecord.getId()
                        + ", 出库重量: " + toOut
                        + ", 入库日期: " + storageIn.getInDate()
                        + ", 出库日期: " + storageOut.getOutDate()
                        + ", 天数: " + days
                        + ", 免费天数: " + freeDays
                        + ", 费率: " + feeRate);

                BigDecimal fee = portRuleCalculationService.calculateStorageFee(
                        BigDecimal.valueOf(toOut),
                        (int) days,
                        freeDays,
                        feeRate
                );
                totalStorageFee = totalStorageFee.add(fee);
            }

            // 更新库存记录...
            BigDecimal newRemainingWeight = stockRecord.getRemainingWeight().subtract(BigDecimal.valueOf(toOut));
            stockRecordService.updateRemainingWeight(stockRecord.getId(), newRemainingWeight);

            if (newRemainingWeight.compareTo(BigDecimal.ZERO) <= 0) {
                stockRecord.setStatus(0);
                stockRecordService.updateStockRecord(stockRecord);
            }

            warehouseAllocationService.updateWarehouseCapacity(stockRecord.getWarehouseId(), -toOut);
            remainWeight -= toOut;
        }

        if (remainWeight > 0) {
            throw new RuntimeException(String.format(
                    "库存不足！请求出库 %.2f 吨，但该公司在该港口下总库存只有 %.2f 吨，还缺少 %.2f 吨",
                    totalOutWeight, totalAvailableStock, remainWeight
            ));
        }

        return totalStorageFee;
    }

    @Override
    public StorageOut getStorageOutById(Long id) {
        return storageOutMapper.selectById(id);
    }

    @Override
    public List<StorageOut> getAllStorageOut() {
        return storageOutMapper.selectAll();
    }

    @Override
    public int updateStorageOut(StorageOut storageOut) {
        return storageOutMapper.update(storageOut);
    }

    @Override
    public int deleteStorageOut(Long id) {
        return storageOutMapper.deleteById(id);
    }

    @Override
    public List<StorageOut> getByOrderNumber(String orderNumber) {
        return storageOutMapper.selectByOrderNumber(orderNumber);
    }

    @Override
    public List<StorageOut> getStorageOutByPortId(Long portId) {
        return storageOutMapper.selectByPortId(portId);
    }
    
    @Override
    public Double sumOutWeightByInId(Long inId) {
        return storageOutMapper.sumOutWeightByInId(inId);
    }

    @Override
    public int batchAddStorageOut(List<StorageOut> storageOutList) {
        int successCount = 0;
        for (StorageOut storageOut : storageOutList) {
            try {
                addStorageOut(storageOut);
                successCount++;
            } catch (Exception e) {
                // 遇到异常立即停止
                break;
            }
        }
        return successCount;
    }

    @Override
    public int deleteAll() {
        return storageOutMapper.deleteAll();
    }
} 